package goo

import (
	"sync"
)

var interceptor *Interceptor
var onceInterceptor sync.Once

type Interceptor struct {
	interceptors       map[string][]func(*Invocation)
	globalInterceptors []func(*Invocation)
	_interceptor       *_Interceptor
	_interceptor2      *_Interceptor2
	_interceptor3      *_Interceptor3
}

func InterceptorInstance() *Interceptor {
	onceInterceptor.Do(func() {
		interceptor = &Interceptor{interceptors: make(map[string][]func(*Invocation)),
			globalInterceptors: make([]func(*Invocation), 0),
			_interceptor:       &_Interceptor{}, _interceptor2: &_Interceptor2{}, _interceptor3: &_Interceptor3{}}
	})
	return interceptor
}

func (it *Interceptor) AddInterceptor(pattern string, interceptors []func(*Invocation)) {
	if interceptors == nil || len(interceptors) == 0 {
		return
	}
	thisInterceptors, ok := it.interceptors[pattern]
	if !ok {
		thisInterceptors = make([]func(*Invocation), 0)
	}
	thisInterceptors = append(thisInterceptors, interceptors...)
	it.interceptors[pattern] = thisInterceptors
}

func (it *Interceptor) AddInterceptorBefore(pattern string, interceptors []func(*Invocation)) {
	if interceptors == nil || len(interceptors) == 0 {
		return
	}
	thisInterceptors, ok := it.interceptors[pattern]
	if !ok {
		thisInterceptors = make([]func(*Invocation), 0)
	}
	thisInterceptors = append(interceptors, thisInterceptors...)
	it.interceptors[pattern] = thisInterceptors
}

func (it *Interceptor) AddGlobalIntercept(interceptors ...func(*Invocation)) {
	if interceptors == nil || len(interceptors) == 0 {
		return
	}
	it.globalInterceptors = append(it.globalInterceptors, interceptors...)
}

func (it *Interceptor) GetInterceptors(pattern string) []func(*Invocation) {
	its, ok := it.interceptors[pattern]
	if ok {
		return append(it.globalInterceptors, its...)
	}
	return it.globalInterceptors
}

type Invocation struct {
	Ctx         *Context
	Handle      func(ctx *Context)
	Interceptor func(inv *Invocation)
	invocation  *Invocation
}

func (inv *Invocation) Invoke() {
	if inv.Interceptor != nil {
		inv.Interceptor(inv.invocation)
	} else if inv.Handle != nil {
		inv.Handle(inv.Ctx)
	}
}

type _Interceptor struct{}
type _Interceptor2 struct{}
type _Interceptor3 struct{}

//route.Add(..).Intercept
func (it *_Interceptor) Intercept(interceptor ...func(*Invocation)) {
	patterns := RoutesInstance().controllerMethodPattern
	for _, p := range patterns {
		InterceptorInstance().AddInterceptorBefore(p, interceptor)
	}
	RoutesInstance().controllerMethodPattern = make([]string, 0)
}

//web.Get(..).Intercept
func (it *_Interceptor2) Intercept(interceptor ...func(*Invocation)) {
	pattern := RoutesInstance().pattern
	InterceptorInstance().AddInterceptor(pattern, interceptor)
}

//web.Intercept
func (it *_Interceptor3) Intercept(interceptors ...func(*Invocation)) {
	if RoutesInstance().inControllerMethod {
		controllerInterceptor := RoutesInstance().controllerInterceptor
		controllerInterceptor = append(controllerInterceptor, interceptors...)
		RoutesInstance().controllerInterceptor = controllerInterceptor
	} else {
		InterceptorInstance().AddGlobalIntercept(interceptors...)
	}
}
